package com.wiscamp.ninechapters.problems.application.services;

import com.wiscamp.ninechapters.common.core.CollectionHelper;
import com.wiscamp.ninechapters.problems.application.assemblers.ProblemAssembler;
import com.wiscamp.ninechapters.problems.application.assemblers.ProblemSetAssembler;
import com.wiscamp.ninechapters.problems.application.dto.commands.CreateProblemSetCommand;
import com.wiscamp.ninechapters.problems.application.dto.commands.sets.*;
import com.wiscamp.ninechapters.problems.application.dto.vo.problems.ProblemVO;
import com.wiscamp.ninechapters.problems.application.dto.vo.sets.ProblemSetVO;
import com.wiscamp.ninechapters.problems.domain.repositories.ProblemSetRepository;
import com.wiscamp.ninechapters.problems.domain.services.ProblemService;
import com.wiscamp.ninechapters.problems.domain.services.ProblemSetService;
import cube.ddd.application.BaseAppService;
import cube.ddd.application.command.DeleteCommand;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.common.query.PagedObjects;
import cube.ddd.common.query.PagedQuery;
import cube.ddd.utils.AppServiceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Objects;

@Service
public class ProblemSetAppService extends BaseAppService {

    private final ProblemSetRepository problemSetRepository;
    private final ProblemSetService problemSetService;
    private final ProblemService problemService;

    public ProblemSetAppService(ProblemSetRepository problemSetRepository, ProblemSetService problemSetService, ProblemService problemService) {
        this.problemSetRepository = problemSetRepository;
        this.problemSetService = problemSetService;
        this.problemService = problemService;
    }

    @HandleServiceException
    public ProblemSetVO getProblemSet(@Min(0) long id) {
        return AppServiceUtils.getById(id, problemSetService::getProblemSet,
                ProblemSetAssembler.Instance::toProblemSetVO);
    }

    @HandleServiceException
    public Collection<ProblemSetVO> getProblemSetsByOrganization(@Min(0) long organizationId) {
        return AppServiceUtils.getListById(organizationId, problemSetRepository::getProblemSetsByOrganization,
                ProblemSetAssembler.Instance::toProblemSetVO);
    }

    @HandleServiceException
    public ProblemSetVO getProblemSetByCode(@NotBlank String setCode, @Min(0) long organizationId) {
        return AppServiceUtils.getByTextAndId(setCode, organizationId, problemSetRepository::getProblemSetByCode,
                ProblemSetAssembler.Instance::toProblemSetVO);
    }

    @HandleServiceException
    public Collection<ProblemSetVO> getProblemSetsByName(@NotBlank String setName, @Min(0) long organizationId) {
        return AppServiceUtils.getListByTextAndId(setName, organizationId, problemSetRepository::getProblemSetsByName,
                ProblemSetAssembler.Instance::toProblemSetVO);
    }

    @HandleServiceException
    public PagedObjects<ProblemSetVO> getProblemSetsByQuery(@NotNull PagedQuery query) {
        var pagedObjects = problemSetRepository.getProblemSetsByQuery(query);
        return AppServiceUtils.convertPagedObjects(pagedObjects, ProblemSetAssembler.Instance::toProblemSetVO);
    }

    @HandleServiceException
    public boolean deleteProblemSet(@NotNull DeleteCommand command) {
        return AppServiceUtils.delete(command.getId(), command.getOperatorId(), problemSetService::deleteProblemSet);
    }

    @HandleServiceException
    public ProblemSetVO createProblemSet(@NotNull CreateProblemSetCommand command) {
        return AppServiceUtils.createOrUpdateBy(command, command.getOperatorId(), problemSetService::createProblemSet,
                ProblemSetAssembler.Instance::toProblemSetVO, ProblemSetAssembler.Instance::fromCreateCommand);

    }

    @HandleServiceException
    public ProblemSetVO updateProblemSet(@NotNull UpdateProblemSetCommand command) {
        return AppServiceUtils.createOrUpdateBy(command, command.getOperatorId(), problemSetService::updateProblemSet,
                ProblemSetAssembler.Instance::toProblemSetVO, ProblemSetAssembler.Instance::fromUpdateCommand);
    }

    @HandleServiceException
    public String exportProblemSet(@Min(0) long setId) {
        return problemSetService.exportProblemSet(setId);
    }

    @HandleServiceException
    public boolean changeProblemSetName(@NotNull ChangeProblemSetNameCommand command) {
        command.setOperatorId(getCurrentUserId());
        return problemSetService.changeProblemSetName(command.getSetId(), command.getName(), command.getOperatorId());
    }

    @HandleServiceException
    public ProblemSetVO copyProblemSet(@NotNull CopyProblemSetCommand command) {
        command.setOperatorId(getCurrentUserId());
        var set = problemSetService.copyProblemSet(command.getSetId(), command.getOperatorId());
        if (Objects.nonNull(set))
            return ProblemSetAssembler.Instance.toProblemSetVO(set);
        return null;
    }

    @HandleServiceException
    public ProblemSetVO createProblemSetByCollection(@NotNull CreateProblemSetByCollectionCommand command) {
        command.setOperatorId(getCurrentUserId());
        var set = problemSetService.createProblemSetByCollection(command.getCollectionCode(), command.getOrganizationId(), command.getOperatorId());
        if (Objects.nonNull(set))
            return ProblemSetAssembler.Instance.toProblemSetVO(set);
        return null;
    }

    @HandleServiceException
    public ProblemSetVO createProblemSetByRule(@NotNull CreateProblemSetByRuleCommand command) {
        command.setOperatorId(getCurrentUserId());
        var set = problemSetService.createProblemSetByRule(command.getRuleId(), command.getOperatorId());
        if (Objects.nonNull(set))
            return ProblemSetAssembler.Instance.toProblemSetVO(set);
        return null;
    }

    @HandleServiceException
    public ProblemSetVO createProblemSetByCustomText(@NotNull CreateProblemSetByCustomTextCommand command) {
        command.setOperatorId(getCurrentUserId());
        var set = problemSetService.createProblemSetByCustomText(command.getSetName(), command.getTexts(),
                command.getOrganizationId(), command.getOperatorId());
        if (Objects.nonNull(set))
            return ProblemSetAssembler.Instance.toProblemSetVO(set);
        return null;
    }

    @HandleServiceException
    public ProblemSetVO createOrAddToProblemSetByProblems(@NotNull CreateOrAddToProblemSetCommand command) {
        command.setOperatorId(getCurrentUserId());
        var set = problemSetService.createOrAddToProblemSetByProblems(command.getProblemIds(), command.isNewSet(),
                command.getOldSetName(), command.getOrganizationId(), command.getOperatorId());
        if (Objects.nonNull(set))
            return ProblemSetAssembler.Instance.toProblemSetVO(set);
        return null;
    }

    /*@HandleServiceException
    public Collection<ProblemVO> getProblemsByCustomQuery(@NotNull CustomProblemQuery query) {
        return null;
    }*/

    @HandleServiceException
    public Collection<Long> getProblemIdsBySet(long setId) {
        return problemSetRepository.getProblemIdsBySet(setId);
    }

    @HandleServiceException
    public Collection<ProblemVO> getProblemsBySet(long setId) {
        var problemIds = problemSetRepository.getProblemIdsBySet(setId);
        var problems = problemService.getProblemsByIds(problemIds);
        return CollectionHelper.convertToArrayList(problems, ProblemAssembler.Instance::toProblemVO);
    }

    @HandleServiceException
    public boolean removeSetProblem(@NotNull RemoveSetProblemCommand command) {
        return problemSetService.removeSetProblem(command.getSetId(), command.getProblemId(), command.getOperatorId());
    }
}
